package c2.group.business.gradepvp;

import c2.group.business.base.GroupSubModule;
import cate.common.game.mail.MailCmd;
import cate.common.table.d.GDGradePvp;
import cate.common.table.d.GDOperation;
import cate.common.table.pvp.gradepvp.row.GradePvpGodRow;
import cate.common.table.pvp.gradepvp.row.GradePvpLevelRow;
import cate.common.table.pvp.gradepvp.row.GradePvpRewardRow;
import cate.common.table.pvp.gradepvp.row.GradePvpRobotRow;
import cate.game.play.part.PlaySide;
import cate.game.pvp.gradepvp.dto.GradePvpPlayerData;
import cate.game.role.pvp.gradepvp.GradePvpPlayer;
import cp.solution.business.game.component.NoticeParam;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

public class GradePvpPoolModule extends GroupSubModule<GradePvpModule> {
    public Map<String, GradePvpPlayer> players = new HashMap<>();
    public TreeMap<Integer, LinkedHashMap<String, GradePvpPlayer>> starPlayers = new TreeMap<>();
    public SortedMap<Integer, Map<String, GradePvpPlayer>> robots = new TreeMap<>();
    private final Object poolLock = new Object();

    @Override
    public void initDependent() {
        List<GradePvpPlayer> poolPlayers = useDb().gradePvp.getPoolPlayers();
        if (!poolPlayers.isEmpty()) {
            for (GradePvpPlayer player : poolPlayers) {
                players.put(player.ps.snapshot.uid, player);
                computeStarPlayers(player);
            }
        }
        loadRobots();
    }

    public int loadMyRank(String roleUid) {
        int rank = -1;
        synchronized (poolLock) {
            for (Map<String, GradePvpPlayer> entry : starPlayers.descendingMap().values()) {
                for (GradePvpPlayer player : entry.values().stream().sorted(Comparator.comparingInt(e -> -e.score)).collect(Collectors.toList())) {
                    rank ++;
                    if (StringUtils.equals(roleUid, player.ps.snapshot.uid)) {
                        return rank;
                    }
                }
            }
        }
        return -1;
    }

    public void updatePlayers(PlaySide ps, int star, int score) {
        synchronized (poolLock) {
            GradePvpPlayer player = players.get(ps.snapshot.uid);
            if (player != null) {
                player.ps = ps;
                if (star != player.star) {
                    Map<String, GradePvpPlayer> oldPlayers = starPlayers.get(player.star);
                    if (oldPlayers != null) {
                        oldPlayers.remove(player.ps.snapshot.uid);
                    }
                    player.star = star;
                    computeStarPlayers(player);
                }
            } else {
                player = new GradePvpPlayer();
                player.ps = ps;
                player.star = star;
                player.score = score;
                players.put(ps.snapshot.uid, player);
                computeStarPlayers(player);
            }
        }
    }

    private void loadRobots() {
        List<GradePvpRobotRow> robotList  = context.table.gradePvp.robot.getList();
        robotList.forEach(v ->{
            EcResult<PlaySide> psRes = context.play.sideBuilder.createPlaySideByCode(v.instance);
            if (!psRes.ok()) {
                return;
            }
            GradePvpPlayer robot = new GradePvpPlayer();
            robot.star = v.star;
            robot.ps = psRes.data;
            computeStarRobots(robot);
        });
    }

    public GradePvpPlayerData match(String playerUid, int star)  {
        GradePvpRewardRow starRow = context.table.gradePvp.reward.getRowByStar(star);
        if (starRow == null) {
            return null;
        }
        int maxStar = 10000;
        GradePvpLevelRow nextStarRow = context.table.gradePvp.level.getNextRowByStar(star);
        if (nextStarRow != null) {
            maxStar = nextStarRow.star;
        }
        GradePvpPlayerData data = new GradePvpPlayerData();
        GradePvpPlayer oppo;
        synchronized (poolLock) {
            List<GradePvpPlayer> oppos = starPlayers.subMap(starRow.star, maxStar).values().stream()
                    .flatMap(e -> e.values().stream())
                    .filter(e -> !StringUtils.equals(playerUid, e.ps.snapshot.uid))
                    .collect(Collectors.toList());
            if (oppos.isEmpty()) {
                oppos = robots.subMap(starRow.star, maxStar).values().parallelStream()
                        .flatMap(e -> e.values().stream())
                        .collect(Collectors.toList());
                Collections.shuffle(oppos);
                oppo = oppos.get(0);
                data.rank = -1;
            } else {
                Collections.shuffle(oppos);
                oppo = oppos.get(0);
                data.rank = loadMyRank(oppo.ps.snapshot.uid);
            }
            data.player = oppo;
            return data;
        }
    }

    private void computeStarPlayers(GradePvpPlayer player) {
        Map<String, GradePvpPlayer> groupStarPlayers = starPlayers.computeIfAbsent(player.star, k -> new LinkedHashMap<>());
        groupStarPlayers.put(player.ps.snapshot.uid, player);
    }

    private void computeStarRobots(GradePvpPlayer player) {
        Map<String, GradePvpPlayer> groupStarPlayers = robots.computeIfAbsent(player.star, k -> new LinkedHashMap<>());
        groupStarPlayers.put(player.ps.snapshot.uid, player);
    }

    public List<GradePvpPlayer> sixGod() {
        List<GradePvpPlayer> list = new ArrayList<>();
        synchronized (poolLock) {
            for (Map<String, GradePvpPlayer> entry : starPlayers.descendingMap().values()) {
                if (list.size() >= 6) {
                    break;
                }
                for (GradePvpPlayer player : entry.values().stream().sorted(Comparator.comparingInt(e -> -e.score)).collect(Collectors.toList())) {
                    if (list.size() < 6) {
                        GradePvpGodRow godRow = context.table.gradePvp.god.get(list.size() + 1);
                        if (godRow != null) {
                            List<NoticeParam> params = new ArrayList<>();
                            params.add(new NoticeParam(godRow.id));
                            MailCmd mail = new MailCmd()
                                    .withContent(GDGradePvp.GOD_REWARD_MAIL_ID, params)
                                    .withReward(godRow.rewardStr)
                                    .withOpr(GDOperation.GRADE_PVP_GOD_REWARD);
                            mail.addRole(player.ps.snapshot.uid).withSrvId(player.ps.snapshot.base.srvId);
                            context.common.sendMail(mail);
                        }
                        list.add(player);
                    } else {
                        break;
                    }
                }
            }
        }
        return list;
    }

    public void reset() {
        synchronized (poolLock) {
            players.clear();
            starPlayers.clear();
        }
    }

    @Override
    public void onShutdown() throws Exception {
        super.onShutdown();
        useDb().gradePvp.savePlayers(players.values());
    }
}
